package main

import (
	"fmt"
	"github.com/spf13/pflag"
	"github.com/spf13/viper"
	"log"
	"time"
)

var (
	cfg  = pflag.StringP("config", "c", "", "Configuration file.")
	help = pflag.BoolP("help", "h", false, "Show this help message.")

	user = pflag.StringP("user", "u", "", "User name.")
	port = pflag.IntP("port", "p", 7979, "A Age")
)

func main() {
	pflag.Parse()
	if *help {
		pflag.Usage()
		return
	}

	initConfig()
	bindFlagAndEnv()

	readConfig()
	reflectConfig()
}

// 从配置文件中读取配置
func initConfig() {
	// 如果指定了配置文件名，则使用指定的配置文件，否则在注册的搜索路径中搜索
	if *cfg != "" {
		viper.SetConfigFile(*cfg)   // 指定配置文件名
		viper.SetConfigType("toml") // 如果配置文件名中没有文件扩展名，则需要指定配置文件的格式，告诉viper以何种格式解析文件
	} else {
		viper.AddConfigPath(".")      // 把当前目录加入到配置文件的搜索路径中
		viper.AddConfigPath("$HOME")  // 配置文件搜索路径，可以设置多个配置文件搜索路径
		viper.SetConfigName("config") // 配置文件名称（没有文件扩展名）
	}

	// 读取配置文件
	//if err := viper.ReadInConfig(); err != nil {
	//	panic(fmt.Errorf("配置文件不存在或解析错误: %s \n", err))
	//}

	if err := viper.ReadInConfig(); err != nil {
		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
			log.Println("配置文件不存在")
		} else {
			panic(fmt.Errorf("配置解析错误: %s \n", err))
		}
	}

	log.Printf("Used configuration file is: %s\n", viper.ConfigFileUsed())
}

// 绑定命令行和环境变量
func bindFlagAndEnv() {
	// 绑定命令行
	// viper.BindPFlags(pflag.CommandLine)
	viper.BindPFlag("port", pflag.Lookup("port"))
	viper.BindPFlag("section_config.user", pflag.Lookup("user"))

	// 绑定的环境变量都会自动转为大写
	viper.SetEnvPrefix("http") // 设置环境变量前缀：HTTP_，如果是http，将自动转变为大写。
	viper.BindEnv("port")
}

// 读取最终配置
func readConfig() {
	// 命令行参数port -> 环境变量 HTTP_PORT -> 配置文件 port
	fmt.Printf("http_port:%v\n", viper.GetInt("port"))
	fmt.Printf("User Name:%v\n", viper.GetString("section_config.user"))
	fmt.Printf("Map Config:%v\n", viper.GetStringMap("section_config"))
}

// 读取最终配置到结构体

// 配置格式参考 https://toml.io/en/v1.0.0#filename-extension
type TomlConfig struct {
	Age        int
	Cats       []string
	Pi         float64
	Perfection []int
	DOB        time.Time
	LogExpire  time.Duration `mapstructure:"logExpire"`
	Port       int           `mapstructure:"port"`

	Section_config Section_config `mapstructure:"section_config"`
	List_config    List_config    `mapstructure:"list_config"`
	Map_config     []Map_config   `mapstructure:"map_config"`
}

type Section_config struct {
	User     string `mapstructure:"user"`
	PassWord string `mapstructure:"passWord"`
}

type List_config struct {
	Processes [][]string `mapstructure:"processes"`
}

type Map_config struct {
	Name string `mapstructure:"Name"`
	Sku  int64  `mapstructure:"sku"`
}

func reflectConfig() {
	var c TomlConfig

	err := viper.Unmarshal(&c)
	if err != nil {
		log.Fatalf("unable to decode into struct, %v", err)
	}

	fmt.Printf("http_port:%v\n", c.Port)
	fmt.Printf("User Name:%v\n", c.Section_config.User)
	fmt.Printf("Map Config:%v\n", c.Section_config)
}
